import tokenize
import token
import sys
import os
import inspect

from agentModel import ClassAgent
from agentModel import AttrAgent
from agentModel import MethodAgent

class ParseModule(object):
    def __init__(self):
        self.class_parser = ParseClass()

    def getClassFromModule(self, module):
        """
        @return list of classAgent objects
        """
        list_ = []

        class_obj_list = self._getClassList(module)
        for each_class_obj in class_obj_list:
            list_.append(self.class_parser.parseClass(each_class_obj))

        return list_

    def _getClassList(self, module):
        class_list = []
        for each_obj in dir(module):
            if inspect.isclass(getattr(module, each_obj, None)):
                class_list.append(getattr(module, each_obj))

        return class_list


class ParseClass(object):
    def parseClass(self, class_):
        # get module.name and class.name
        self.class_ = class_
        self.module_name = class_.__module__
        self.class_name = class_.__name__

        self.agent = ClassAgent('%s.%s' %
                        (self.module_name, self.class_name))

        self._getParent()
        self._handleAttr()
        self._handleMethod()

        return self.agent

    def _handleMethod(self):
        method_list = [each for each in dir(self.class_)
                if not each.startswith('__') and
                    inspect.ismethod(getattr(self.class_, each, None))]

        for method_name in method_list:
            method_obj = getattr(self.class_, method_name)
            if getattr(method_obj, '__module__', None) == self.module_name:
                # new method agent
                method_agent = MethodAgent(method_obj)
                self.agent.method_list.append(method_agent)


    def _handleAttr(self):
        attr_list = [each for each in dir(self.class_)
                if not each.startswith('__') and
                not inspect.ismethod(getattr(self.class_, each, None))]


        # 1 how to get name of attr
        # TODO
        # how to get type of attr
        # how to know the attr belongs to children classes only
        for attr in attr_list:
            attr_agent = AttrAgent(attr, getattr(self.class_, attr, None))
            self.agent.attr_list.append(attr_agent)

    def _getParent(self):
        # get parent
        for each in self.class_.__bases__:
            if each.__module__.startswith('__'):
                break
            parent_module_name = each.__module__
            parent_class_name = each.__name__
            self.agent.parents_name_list.append('%s.%s' %
                                    (parent_module_name, parent_class_name))



class ParseFile(object):
    """
    This class parse a list of files
     - root_module: e.g., presentation.backend, only modules
                    under presentation.backend, e.g. presentation.backend.a
                    will be considered

     - class_agent_list

    Work flow:
     - 
    

    """
    def __init__(self, root=""):
        self.root = root
        self.abs_root = os.path.abspath(root)
        self.class_agent_list = []
        self.root_module = self.abs_root.replace('./', '')\
                                        .replace('.py', '')\
                                        .replace('/', '.')
        self.module_parser = ParseModule()

    def parseFile(self, filename_list):
        for each_file_name in filename_list:
            module_name, pkg_name = self._toModuleName(each_file_name)
            module = __import__(module_name, globals, locals, pkg_name)
            self.class_agent_list.extend(
                self.module_parser.getClassFromModule(module))

        # there may be some duplicated classes
        self._removeDulClass()
        # TODO morethings?

        return self.class_agent_list

    def _toModuleName(self, file_name):
        s = file_name.replace('./', '')
        s = s.replace('.py', '')
        s = s.replace('/', '.')
        s = s.replace(self.root_module, '')
        if s[0] == '.':
            s = s[1:]
        return s, s.split('.')[-1]

    def _removeDulClass(self):
        list_ = []
        existing_class = set()
        for each in  self.class_agent_list:
            if each.getName() in existing_class:
                pass
            else:
                existing_class.add(each.getName())
                list_.append(each)

        self.class_agent_list = list_
